package com.yizisu.playerlibrary.impl.exoplayer

import android.content.Context
import android.media.AudioManager
import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.session.MediaSessionCompat
import android.view.TextureView
import androidx.media.AudioFocusRequestCompat
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.Player.PlayWhenReadyChangeReason
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator
import com.google.android.exoplayer2.video.VideoSize
import com.yizisu.playerlibrary.PlayerFactory
import com.yizisu.playerlibrary.helper.PlayerModel
import com.yizisu.playerlibrary.impl.BaseYzsPlayer
import com.yizisu.playerlibrary.view.video_view.helper.logI
import com.yizisu.playerlibrary.view.video_view.helper.playerLibDebug

/**
 * ExoPlayer实现类，可以用于其他播放器替换
 */
internal class ExoPlayerImpl<Model : PlayerModel>(
    context: Context,
    internal val config: ExoPlayerConfig
) :
    BaseYzsPlayer<Model>(context), Player.Listener {
    //创建播放器
    private val player = config.onCreateExoPlayer(context).apply {
        addListener(this@ExoPlayerImpl)
    }

    override val totalDuration: Long
        get() {
            val duration = player.duration
            return if (duration < 0) {
                0
            } else {
                duration
            }
        }

    override val currentDuration: Long
        get() {
            val duration = player.currentPosition
            return if (duration < 0) {
                0
            } else {
                duration
            }
        }

    override val currentBufferDuration: Long
        get() {
            val duration = player.bufferedPosition
            return if (duration < 0) {
                0
            } else {
                duration
            }
        }

    override fun prepare(
        models: MutableList<Model>,
        playIndex: Int,
        isStopLastMedia: Boolean,
        positionMs: Long/*当前播放进度*/,
        playWhenReady: Boolean
    ) {
        super.prepare(models, playIndex, isStopLastMedia, positionMs, playWhenReady)
        startPrepare(isStopLastMedia, positionMs)
    }

    override fun play() {
        super.play()
        //判断是否启用了音频焦点处理
        if (!requestAudioFocus()) {
            changePlayStatus(true)
        }
    }

    override fun stop() {
        pause()
        player.stop()
        doPlayerListener {
            it.onStop(player.playWhenReady, currentPlayModel)
        }
    }

    override fun pause() {
        super.pause()
        changePlayStatus(false)
    }

    override fun previous(positionMs: Long) {
        if (!canPlayPrevious()) {
            pause()
        } else {
            currentIndex--
            startPrepare(true, positionMs)
            startPlayIfNotPlay()
            doPlayerListener {
                it.onPrevious(currentPlayModel)
            }
        }
    }

    override fun next(positionMs: Long) {
        //如果播放的是最后一个，播放完之后就停止播放
        if (!canPlayNext()) {
            pause()
        } else {
            currentIndex++
            startPrepare(true, positionMs)
            startPlayIfNotPlay()
            doPlayerListener {
                it.onNext(currentPlayModel)
            }
        }
    }

    override fun canPlayNext(): Boolean {
        val index = getCurrentPlayIndex()
        return !(getRepeatMode() == PlayerFactory.LOOP_MODO_NONE && (index + 1) >= playModelList.count())
    }

    override fun canPlayPrevious(): Boolean {
        val index = getCurrentPlayIndex()
        return !(getRepeatMode() == PlayerFactory.LOOP_MODO_NONE && index <= 0)
    }

    override fun retry(isKeepProgress: Boolean) {
        val model = currentPlayModel ?: return
        //获取当前播放进度
        val progress = model.currentDuration
        if (isPlaying()) {
            startTick()
        }
        startPrepare(true, if (isKeepProgress) progress else 0)
    }

    /**
     * 准备资源和播放
     */
    private var lastPlayModel: Model? = null
    private fun startPrepare(isStopLastMedia: Boolean, progress: Long) {
        val ctx = context
        //切换资源的时候，回调上一次资源的销毁方法，做好资源回收
        val last: Model? = lastPlayModel
        if (last != currentPlayModel) {
            last?.onPlayModelNotThis()
            last?.onPlayModelNotThis(currentPlayModel)
            lastPlayModel = currentPlayModel
        }
        if (isStopLastMedia) {
            player.stop()
        }
        currentPlayModel?.apply {
            doPlayerListener {
                it.onPlayerModelChange(this)
                if (last != this) {
                    it.onPlayerModelChange(last, this)
                }
                tickOnMainThread(this, it)
            }
            //回调准备资源监听
            currentPlayModel?.let { model ->
                doPlayerListener {
                    it.onPrepare(model)
                }
            }
            player.createSingleSource(
                ctx,
                this,
                this@ExoPlayerImpl,
                null
            ) { error, isCallOnPlayChange ->
                if (currentPlayModel == this) {//因为这里可能异步，需要判断
                    if (error != null) {//错误监听
                        prepareMediaItemWithError(error)
                    } else {
                        if (progress > 0) {
                            seekTo(progress)
                        }
                        doPlayerListener {
                            it.onPrepareComplete(this)
                        }
                        //因为获取url是异步的，而且还有可能从网络获取其他信息
                        //所以获取url后再通知一次
                        if (isCallOnPlayChange) {
                            doPlayerListener {
                                it.onPlayerModelChange(this)
                                if (last != this) {
                                    it.onPlayerModelChange(last, this)
                                }
                                tickOnMainThread(this, it)
                            }
                        }
                    }
                }
            }
        }
    }

    override fun attachView(view: TextureView?) {
        player.setVideoTextureView(view)
    }

    override fun clearView(view: TextureView?) {
        player.clearVideoTextureView(view)
    }

    override fun seekTo(positionMs: Long, index: Int?) {
        val currentIndex = getCurrentPlayIndex()
        if (index == null || index == currentIndex) {
            player.seekTo(positionMs)
        } else {
            //注意这里，这里赋值，不受播放模式判断影响，所以直接赋值
            info.currentIndex = index
            startPrepare(true, positionMs)
            startPlayIfNotPlay()
        }
        currentPlayModel?.let { model ->
            doPlayerListener {
                tickOnMainThread(model, it)
            }
        }
    }

    private fun startPlayIfNotPlay() {
        if (!isPlaying()) {
            play()
        }
    }

    override fun seekRatioTo(ratio: Float) {
        seekTo((totalDuration * ratio).toLong(), null)
    }

    /**
     * 播放出错
     */
    override fun onPlayerError(error: PlaybackException) {
        logI("onPlayerError uri:${player.currentMediaItem?.playbackProperties?.uri} error:${error.message},code:${error.errorCode}")
        prepareMediaItemWithError(error)
    }


    override fun onPlayerErrorChanged(error: PlaybackException?) {
        super.onPlayerErrorChanged(error)
        if (error != null) {
            logI("onPlayerErrorChanged uri:${player.currentMediaItem?.playbackProperties?.uri} error:${error.message},code:${error.errorCode}")
        }
    }

    override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters) {
        super.onPlaybackParametersChanged(playbackParameters)
    }

    override fun onEvents(player: Player, events: Player.Events) {
        super.onEvents(player, events)
    }

    override fun onTimelineChanged(timeline: Timeline, reason: Int) {
        super.onTimelineChanged(timeline, reason)
    }

    /**
     * 失败之后的重试
     */
    private fun prepareMediaItemWithError(playerError: Throwable) {
        val model = currentPlayModel
        if (model == null) {
            cancelTick()
            return
        }
        player.createSingleSource(context, model, this, playerError) { error, isCallOnPlayChange ->
            if (currentPlayModel == model) {//因为这里可能异步，需要判断
                if (error != null) {//错误监听
                    cancelTick()
                    model.onError(error)
                    doPlayerListener {
                        it.onError(error, model)
                    }
                } else {
                    seekTo(model.currentDuration)
                    //因为获取url是异步的，而且还有可能从网络获取其他信息
                    //所以获取url后再通知一次
                    if (isCallOnPlayChange) {
                        doPlayerListener {
                            it.onPlayerModelChange(model)
                            tickOnMainThread(model, it)
                        }
                    }
                }
            }
        }
    }

    override fun isPlaying(): Boolean {
        return player.playWhenReady
    }

    override fun isBuffering(ignoredPlayStatus: Boolean): Boolean {
        return if (ignoredPlayStatus) {
            player.playbackState == Player.STATE_BUFFERING
        } else {
            isPlaying() && player.playbackState == Player.STATE_BUFFERING
        }
    }

    override fun setMediaSession(
        session: MediaSessionCompat,
        bundleCall: (Model?) -> MediaDescriptionCompat
    ) {
        MediaSessionConnector(session).apply {
            setQueueNavigator(QueueNavigator(mediaSession, bundleCall))
            setPlayer(player)
        }
    }

    private inner class QueueNavigator(
        mediaSession: MediaSessionCompat,
        val bundleCall: (Model?) -> MediaDescriptionCompat
    ) : TimelineQueueNavigator(mediaSession) {
        override fun getMediaDescription(player: Player, windowIndex: Int): MediaDescriptionCompat {
            return bundleCall(currentPlayModel)
        }
    }

    override fun setSingleModelRepeatModel(@PlayerFactory.RepeatMode repeatMode: Int) {
        player.repeatMode = repeatMode
    }

    override fun setHandleWakeLock(handleWakeLock: Boolean) {
        player.setHandleWakeLock(handleWakeLock)
    }

    override fun setWakeMode(@C.WakeMode wakeMode: Int) {
        player.setWakeMode(wakeMode)
    }

    override fun setVolume(volume: Float) {
        player.volume = volume
    }

    override fun getVolume(): Float {
        return player.volume
    }

    override fun setVideoSpeed(speed: Float) {
        super.setVideoSpeed(speed)
        player.playbackParameters = PlaybackParameters(speed)
    }

    override fun onDestroy() {
        player.stop()
        player.clearMediaItems()
        player.release()
        super.onDestroy()
    }

    override fun getAudioForceListener(): AudioManager.OnAudioFocusChangeListener {
        return audioFocusListener
    }

    /**
     * STATE_IDLE, STATE_BUFFERING, STATE_READY, STATE_ENDED
     */
    override fun onPlaybackStateChanged(@Player.State state: Int) {
        super.onPlaybackStateChanged(state)
        val playWhenReady = player.playWhenReady
        val playbackState = when (state) {
            Player.STATE_BUFFERING -> {
                PlayerPlaybackState.STATE_BUFFERING
            }
            Player.STATE_ENDED -> {
                next()
                PlayerPlaybackState.STATE_ENDED
            }
            Player.STATE_IDLE -> {
                PlayerPlaybackState.STATE_IDLE
            }
            Player.STATE_READY -> {
                PlayerPlaybackState.STATE_READY
            }
            else -> {
                PlayerPlaybackState.UN_KNOW
            }
        }
        //是否正在缓冲
        val isBuffer = state == Player.STATE_BUFFERING
        doPlayerListener {
            it.onPlaybackStateChanged(playbackState)
            it.onBufferStateChange(
                isBuffer,
                playWhenReady,
                currentPlayModel
            )
        }
        if (playerLibDebug) {
            logI("onPlayerStateChanged--->${playWhenReady}:${playbackState}")
        }
    }

    /**
     *  PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
     *  PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS,
     *  PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY,
     *  PLAY_WHEN_READY_CHANGE_REASON_REMOTE,
     *  PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM
     */
    override fun onPlayWhenReadyChanged(
        playWhenReady: Boolean,
        @PlayWhenReadyChangeReason reason: Int
    ) {
        super.onPlayWhenReadyChanged(playWhenReady, reason)
        val event = getPlayerPlayWhenReady(reason)
        doPlayerListener {
            it.onPlayWhenReadyChanged(playWhenReady, event)
        }
    }

    /**
     * EXOPlayer监听-播放状态改变
     */
    @Deprecated(
        "过时",
        replaceWith = ReplaceWith("[onPlayWhenReadyChanged],[onPlaybackStateChanged]")
    )
    override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
    }

    /**
     * EXOPlayer监听-播放状态变化
     */
    override fun onIsPlayingChanged(isPlaying: Boolean) {
        logI("onIsPlayingChanged--->isPlaying:${isPlaying}")
        if (isPlaying) {
            doPlayerListener {
                it.onPlay(player.playWhenReady, currentPlayModel)
            }
        } else {
            doPlayerListener {
                it.onPause(player.playWhenReady, currentPlayModel)
            }
        }
    }

    override fun onVideoSizeChanged(videoSize: VideoSize) {
        super.onVideoSizeChanged(videoSize)
        currentPlayModel?._videoWidth = videoSize.width
        currentPlayModel?._videoHeight = videoSize.height
        doPlayerListener {
            it.onVideoSizeChange(
                videoSize.width,
                videoSize.height,
                videoSize.unappliedRotationDegrees,
                videoSize.pixelWidthHeightRatio,
                currentPlayModel
            )
        }
    }

    /**
     * 音频焦点处理
     * 每个对象都会获取一个焦点，创建的其他对象会失去焦点
     */
    private val audioFocusListener =
        AudioManager.OnAudioFocusChangeListener { focusChange ->
            doPlayerListener {
                it.onAudioFocusChange(focusChange)
            }
            when (focusChange) {
                AudioManager.AUDIOFOCUS_GAIN -> {
                    changePlayStatus(true)
                }
                AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
                    changePlayStatus(false)
                }
                AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
                    changePlayStatus(false)
                }
                AudioManager.AUDIOFOCUS_LOSS -> {
                    changePlayStatus(false)
                }
            }
        }

    /**
     * 改变播放状态
     */
    override fun changePlayStatus(isPlaying: Boolean) {
        super.changePlayStatus(isPlaying)
        player.playWhenReady = isPlaying
    }

    override fun onRenderedFirstFrame() {
        doPlayerListener {
            val currentModel = getCurrentModel()
            it.onRenderedFirstFrame(currentModel)
        }
    }

    override fun getTickPeriod(): Long {
        return config.getTickPeriod()
    }

    override fun getTickOnThread(): Boolean {
        return config.getTickOnThread()
    }

    override fun onCreateAudioFocusRequestCompat(): AudioFocusRequestCompat {
        return config.onCreateAudioFocusRequestCompat(getAudioForceListener())
    }
}